package com.github.gdxmap.layer.format;

import android.content.Context;
import android.graphics.Matrix;
import android.util.Log;
import android.webkit.WebView;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.math.Matrix3;
import com.badlogic.gdx.math.Path;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.github.gdxmap.core.GdxMap;
import com.github.gdxmap.geo.projection.MercatorProjection;
import com.github.gdxmap.geometry.Point;
import com.github.gdxmap.layer.vector.Polygon;
import com.github.gdxmap.layer.vector.Polyline;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;

import okio.BufferedSource;
import okio.Okio;

/**
 * Created by free on 2016/12/6 0006.
 */
public class GeoJsonLayer extends VectorLayer {
    public static final String Point = "Point";
    public static final String LineString = "LineString";
    public static final String Polygon = "Polygon";
    public static final String MultiPoint = "MultiPoint";
    public static final String MultiLineString = "MultiLineString";
    public static final String MultiPolygon = "MultiPolygon";
    public static final String GeometryCollection = "GeometryCollection";

    private URI uri;
    //geojson内容
    private String content;

    private Context context;

    private MercatorProjection mercatorProjection;

    List<Polygon> polygons = new ArrayList<>(20);
    List<Polyline> polylines = new ArrayList<>(20);

    float minX = Float.MAX_VALUE;
    float minY = Float.MAX_VALUE;
    float maxX = 0;
    float maxY = 0;

    public GeoJsonLayer(Context context) {
        this.context = context;

        this.mercatorProjection = new MercatorProjection("epsg:4326", "epsg:3395");
    }

    public GeoJsonLayer(Context context, URI uri) {
        this.context = context;

        this.uri = uri;

        this.mercatorProjection = new MercatorProjection("epsg:4326", "epsg:3395");
    }

    public GeoJsonLayer(Context context, File file) {
        this.context = context;

        uri = file.toURI();

        this.mercatorProjection = new MercatorProjection("epsg:4326", "epsg:3395");
    }

    @Override
    public void addRender() {
        super.addRender();

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                getContentForURI();
            }
        };

        Thread thread = new Thread(runnable);
        thread.start();
    }

    private void getContentForURI() {
        if (uri == null) {
            throw new RuntimeException("数据源不能为空");
        }
        if (uri.getScheme().equals("file")) {
            String path = uri.getPath();

            if (path.contains("android_asset")) {
                loadAssetFile(path.replace("/android_asset/", ""));
            }
        }
        geometryToLayer();

        Gdx.graphics.requestRendering();
    }

    private void loadAssetFile(String filePath) {
        InputStream is = null;
        BufferedSource source = null;
        try {
            is = context.getAssets().open(filePath);
            source = Okio.buffer(Okio.source(is));

            content = source.readUtf8();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (source != null) {
                    source.close();
                }
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void geometryToLayer() {
        Gson gson = new Gson();

        JsonObject geojson = gson.fromJson(content, JsonObject.class);

        for (JsonElement feature : geojson.getAsJsonArray("features")) {
            JsonObject geometry = feature.getAsJsonObject().getAsJsonObject("geometry");

            switch (geometry.get("type").getAsString()) {
                case Point:
                    break;
                case MultiPoint:
                    break;
                case LineString:
                    decodePolyLine(geometry);
                    break;
                case MultiLineString:
                    break;
                case Polygon:
                    decodePolygon(geometry,false);
                    break;
                case MultiPolygon:
                    decodePolygon(geometry,true);
                    break;
                case GeometryCollection:
                    break;
            }
        }
        for (com.github.gdxmap.layer.vector.Polygon polygon : polygons) {
            float[] vec = polygon.getVertices();

            translate(vec);

            com.github.gdxmap.layer.vector.Polygon changedPolygon = new Polygon(vec);

            getVectorLayerNodeList().add(changedPolygon);
        }
        for (Polyline polyline : polylines) {
            float[] vec = polyline.getVertices();

            translate(vec);

            Polyline changedPolyline = new Polyline(vec);

            getVectorLayerNodeList().add(changedPolyline);
        }
    }

    private void decodePolygon(JsonObject geometry, boolean isMulti) {
        JsonArray coordinates = geometry.getAsJsonArray("coordinates");

        List<Point> points = new ArrayList<>(20);

        for (JsonElement child : coordinates) {
            for (JsonElement childPart : child.getAsJsonArray()) {
                if (isMulti) {
                    List<Point> childPoints = new ArrayList<>(10);

                    for (JsonElement multiChildPart : childPart.getAsJsonArray()) {
                        com.github.gdxmap.geometry.Point point = mercatorProjection.project(multiChildPart.getAsJsonArray().get(0).getAsDouble(), multiChildPart.getAsJsonArray().get(1).getAsDouble());

                        childPoints.add(point);

                        compareBound(point);
                    }

                    com.github.gdxmap.layer.vector.Polygon polygon = new Polygon(childPoints);

                    this.polygons.add(polygon);
                } else {
                    com.github.gdxmap.geometry.Point point = mercatorProjection.project(childPart.getAsJsonArray().get(0).getAsDouble(), childPart.getAsJsonArray().get(1).getAsDouble());

                    points.add(point);

                    compareBound(point);
                }
            }
        }
        if (!isMulti) {
            com.github.gdxmap.layer.vector.Polygon polygon = new Polygon(points);

            this.polygons.add(polygon);
        }
    }

    private void decodePolyLine(JsonObject geometry) {
        JsonArray coordinates = geometry.getAsJsonArray("coordinates");

        List<Point> points = new ArrayList<>(20);

        for (JsonElement child : coordinates.getAsJsonArray()) {
            com.github.gdxmap.geometry.Point point = mercatorProjection.project(child.getAsJsonArray().get(0).getAsDouble(), child.getAsJsonArray().get(1).getAsDouble());

            compareBound(point);

            points.add(point);
        }

        Polyline polyline = new Polyline(points);

        this.polylines.add(polyline);
    }

    /**
     * 计算包围盒
     *
     * @param point
     */
    private void compareBound(Point point) {
        minX = minX > point.x ? point.x : minX;
        minY = minY > point.y ? point.y : minY;
        maxX = maxX < point.x ? point.x : maxX;
        maxY = maxY < point.y ? point.y : maxY;
    }

    /**
     * 移动和缩放坐标点到屏幕中心
     */
    private void translate(float[] vec) {
        int width = Gdx.graphics.getWidth();
        int height = Gdx.graphics.getHeight();

        if (GdxMap.getMapOptions().getMaxBounds()==null) {
            Vector2 center = new Vector2((minX + maxX) / 2, (minY + maxY) / 2);

            Matrix matrix = new Matrix();
            matrix.postTranslate(width / 2 - center.x, height / 2 - center.y);
            matrix.postScale(width / (maxX - minX), width / (maxX - minX), width / 2, height / 2);
            matrix.mapPoints(vec);

            GdxMap.getMapOptions().setMaxBounds(new Rectangle(minX,minY,maxX-minX,maxY-minY));
        }else {
            Rectangle rectangle=GdxMap.getMapOptions().getMaxBounds();

            Vector2 center = rectangle.getCenter(new Vector2());

            Matrix matrix = new Matrix();
            matrix.postTranslate(width / 2 - center.x, height / 2 - center.y);
            matrix.postScale(width / rectangle.width, width / rectangle.width, width / 2, height / 2);
            matrix.mapPoints(vec);
        }
    }
}
